/**
 *	The NeuroCoSA Toolkit
 *	Copyright (C) 2003-6 Stuart Meikle.
 *
 *	This is free software; you can redistribute it and/or
 *	modify it under the terms of the GNU Lesser General Public
 *	License as published by the Free Software Foundation; either
 *	version 2.1 of the License, or (at your option) any later version.
 *
 *	This library is distributed in the hope that it will be useful,
 *	but WITHOUT ANY WARRANTY; without even the implied warranty of
 *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *	Lesser General Public License for more details.
 *
 * @author	Stuart Meikle
 * @version	2006-halloween(mk2)
 * @license	LGPL
 */
package org.stumeikle.NeuroCoSA.NIS;
import java.io.*;
import org.stumeikle.NeuroCoSA.PubSub.TimeLord;
import org.stumeikle.NeuroCoSA.PubSub.Subscriber;

//20061127 stumeikle
//
//basic idea here is that the subscriber needs to get access to the data from the previous
//update cycle, as that data is the data which has been validated or which has made it to the
//outside world (for neurocosa).
//
//thus if the publication has current and previous values stored:
//
//if the subscriber is updated (in each cycle) before the publisher then it needs the current value 
//if the subscriber is updated after the publisher then it needs the previous value

//20071211 stumeikle
//re-writing this today to base it on super synchronised atom. synchronisation needs to be clearer
//there needs to be a clear distinction between asking for data in the past and asking for current
//data

import java.util.*;

public class	InfoWithSingleStore extends Info 
{
    //store values in a linked list. pop values from the front of the list when they are too old
    //each incoming object will need to be wrapped so that time can also be stored
    class	TimeStampedValue
    {
	Object			iValue;
	long			iTimeStamp;

	TimeStampedValue(Object o, long t) { iValue = o; iTimeStamp = t; }

	long			getTime() { return iTimeStamp; }
	void			setTime(long l ) { iTimeStamp =l; }
	Object			getValue() { return iValue; }
	void			setValue( Object o ) { iValue=o; }
    }

    protected  TimeStampedValue iPreviousValue, iCurrentValue;
    protected  long		iLastUpdateTime;
    protected  long		iLargestDeltaStored; // = fn(iSlowestConsumersDelta)
    protected  long		iValueShelfLife; //how many ticks values remain valid for
    protected  TimeLord		iTimeLord;

    public InfoWithSingleStore(String n, TimeLord t)
    {
	super(n);
	iTimeLord=t;

	iLargestDeltaStored = 1; //we always store now and previous values
	iValueShelfLife = 2; //make sure that we Can read data set last time
	iPreviousValue = iCurrentValue = null;
	iLastUpdateTime = 0;
    }

    //overload the standard methods  [ 20071211 stumeikle - dont this this text is quite true now ]
    //setValue () => store value at current time 
    //getValue () => return value stored at current time if poss else current time -1. 
    //getValue ( time t ) => return value stored at time given - 1. Update slowest consumers delta
    //		             and iLargestDeltaStored as needed. ie increase buffer size for slow 
    //                       consumers
    //updateList() => remove entries from the start of the list which are older than current time - 
    //		      iLargestDeltaStored

    public synchronized void	setValue(Object av)
    {
	long	 now = iTimeLord.getTime();

	//(1) allow us to send multiple updates to this value at the same time.
	//only store the last value.
	lock(this);
	if (now == iLastUpdateTime)
        {
	    //in this case just update the structure at the end of the list
	    iCurrentValue.setValue(av);
	}
	else
	{
	     //time has passed. store the current value to the previous value
	     iPreviousValue = iCurrentValue;
	     TimeStampedValue	t = new TimeStampedValue(av, now);
	     iCurrentValue = t;
	     iLastUpdateTime = now;
	}

	releaseAndNotify(this);
    }

    public synchronized Object	getValue()
    {
	//return value at 'now'
	long 	t = iTimeLord.getTime();
	
	return getValue( t );
    }
    
    public Object		getValue( long t )
    {
	if (iLocker!=null)	//someone is updating the data currently
	{
	    //if someone has locked the data then wait
	    try{ wait() ; } 
	    catch(Exception e){}
	}

	//minor mod to help callers
	if (t<=0)
	{  t+= iTimeLord.getTime(); } 

	//(2) find the best match. we can only return the current value or the previous value if
	//    its still valid
	TimeStampedValue	tsv =  iCurrentValue;
        long			tsvt;

	if (tsv!=null)
	{
	    tsvt = tsv.getTime();

	    if (t >=tsvt && t< (tsvt+iValueShelfLife))
	    {
		return tsv.getValue();
	    }
	}

	tsv = iPreviousValue;
	if (tsv!=null)
	{
	    tsvt = tsv.getTime();

	    if (t >=tsvt && t< (tsvt+iValueShelfLife))
	    {
		return tsv.getValue();
	    }
	}

	//else
	return null;
    }
}


